"""
@Author: li
@Email: lijianqiao2906@live.com
@FileName: system.py
@DateTime: 2025/07/06
@Docs: 系统管理相关服务，包括角色、权限、菜单等
"""

from typing import cast

from tortoise.exceptions import DoesNotExist
from tortoise.transactions import in_transaction

from app.core.exceptions import ConflictException, ForbiddenException
from app.models import LoginLog, Menu, OperationLog, Permission, Role, SystemConfig, UserSettings
from app.schemas import RoleCreate, RoleUpdate, SystemConfigCreate, SystemConfigUpdate, UserSettingsUpdate
from app.services.base import BaseService
from app.services.permission.registry import permission_registry
from app.utils.logger import logger


class RoleService(BaseService):
    """角色服务"""

    def __init__(self):
        super().__init__(Role)

    async def create_role(self, role_in: RoleCreate) -> Role:
        """创建角色"""
        if await self.dao.exists(name=role_in.name):
            raise ConflictException(f"角色名称 '{role_in.name}' 已存在")
        if await self.dao.exists(code=role_in.code):
            raise ConflictException(f"角色编码 '{role_in.code}' 已存在")

        role_data = role_in.model_dump(exclude={"permission_ids"})
        async with in_transaction():
            created_role_model = await self.dao.create(**role_data)
            created_role = cast(Role, created_role_model)
            if role_in.permission_ids:
                await self.assign_permissions(created_role.id, role_in.permission_ids)
        return created_role

    async def update_role(self, role_id: int, role_in: RoleUpdate) -> Role:
        """更新角色"""
        update_data = role_in.model_dump(exclude_unset=True, exclude={"permission_ids"})
        async with in_transaction():
            updated_role_model = await self.dao.update_by_id(role_id, **update_data)
            if not updated_role_model:
                raise DoesNotExist("角色不存在")
            updated_role = cast(Role, updated_role_model)
            if role_in.permission_ids is not None:
                await self.assign_permissions(role_id, role_in.permission_ids)
        return updated_role

    async def assign_permissions(self, role_id: int, permission_ids: list[int]) -> bool:
        """为角色分配权限"""
        try:
            async with in_transaction():
                role_model = await self.dao.get_by_id(role_id)
                if not role_model:
                    raise DoesNotExist("角色不存在")
                role = cast(Role, role_model)
                permissions = await Permission.filter(id__in=permission_ids, is_deleted=False)
                await role.permissions.clear()
                await role.permissions.add(*permissions)
                logger.info(f"角色 {role_id} 分配权限成功: {permission_ids}")
                return True
        except DoesNotExist as e:
            logger.error(f"为角色分配权限失败: {e}")
            raise
        return False


class PermissionService(BaseService):
    """权限服务"""

    def __init__(self):
        super().__init__(Permission)

    async def sync_permissions_from_registry(self) -> int:
        """从权限注册器同步权限到数据库。

        此方法将内存中的权限定义与数据库同步，
        新增或更新权限记录，但不会删除任何权限。
        """
        registered_permissions = permission_registry.get_all_permissions()
        if not registered_permissions:
            logger.warning("权限注册表中没有任何权限，跳过同步。")
            return 0

        synced_count = 0
        async with in_transaction():
            for perm_info in registered_permissions:
                # 准备用于创建或更新的数据，但不包括主键 'id'
                perm_data = {
                    "name": perm_info.name,
                    "description": perm_info.description,
                    "module": perm_info.module,
                    "resource": perm_info.resource,
                    "action": perm_info.action,
                    "type": perm_info.type,
                }
                # 使用 code 作为唯一标识符来查找或创建
                # defaults 参数会在创建新记录时使用
                obj_model, created = await self.dao.get_or_create(defaults=perm_data, code=perm_info.code)
                obj = cast(Permission, obj_model)

                if not created:
                    # 如果记录已存在，则检查是否需要更新
                    has_changed = False
                    for key, value in perm_data.items():
                        if getattr(obj, key) != value:
                            setattr(obj, key, value)
                            has_changed = True
                    if has_changed:
                        await self.dao.update_by_id(obj.id, **perm_data)
                        logger.debug(f"权限已更新: {perm_info.code}")

                synced_count += 1

        logger.info(f"权限同步完成，共处理 {len(registered_permissions)} 个权限定义。")
        return len(registered_permissions)

    async def get_permission_tree(self) -> list[dict]:
        """获取权限树"""
        permissions_models = await self.dao.get_active_all(order_by=["sort_order", "id"])
        permissions = cast(list[Permission], permissions_models)
        permission_dicts = [p.to_dict() for p in permissions]
        return self._build_tree(permission_dicts)

    def _build_tree(self, permissions: list[dict], parent_id: int | None = None) -> list[dict]:
        """递归构建树"""
        branch = []
        for permission in permissions:
            if permission.get("parent_id") == parent_id:
                children = self._build_tree(permissions, permission.get("id"))
                if children:
                    permission["children"] = children
                branch.append(permission)
        return branch


class MenuService(BaseService):
    """菜单服务"""

    def __init__(self):
        super().__init__(Menu)

    async def get_menu_tree(self) -> list[Menu]:
        """获取菜单树"""
        menus_models = await self.dao.get_active_all(order_by=["sort_order"])
        menus = cast(list[Menu], menus_models)
        return self._build_tree(menus)

    def _build_tree(self, menus: list[Menu], parent_id: int | None = None) -> list[Menu]:
        """递归构建菜单树"""
        branch = []
        for menu in menus:
            if menu.parent_id == parent_id:
                children = self._build_tree(menus, menu.id)
                if children:
                    menu.children = children  # type: ignore
                branch.append(menu)
        return branch


class OperationLogService(BaseService):
    """操作日志服务"""

    def __init__(self):
        super().__init__(OperationLog)


class LoginLogService(BaseService):
    """登录日志服务"""

    def __init__(self):
        super().__init__(LoginLog)


class SystemConfigService(BaseService):
    """系统配置服务"""

    def __init__(self):
        super().__init__(SystemConfig)

    async def create_config(self, config_in: SystemConfigCreate) -> SystemConfig:
        """创建配置，并检查key是否唯一"""
        if await self.dao.exists(key=config_in.key):
            raise ConflictException(f"配置键 '{config_in.key}' 已存在")
        config_model = await self.dao.create(**config_in.model_dump())
        return cast(SystemConfig, config_model)

    async def update_config(self, config_id: int, config_in: SystemConfigUpdate) -> SystemConfig:
        """更新配置，并检查是否为只读"""
        config_to_update = await self.get_by_id(config_id)
        if not config_to_update:
            raise DoesNotExist("配置不存在")

        config_to_update = cast(SystemConfig, config_to_update)
        if config_to_update.is_readonly:
            raise ForbiddenException("只读配置项不允许修改")

        updated_config_model = await self.dao.update_by_id(config_id, **config_in.model_dump(exclude_unset=True))
        return cast(SystemConfig, updated_config_model)

    async def delete_config(self, config_id: int) -> None:
        """删除配置，并检查是否为只读"""
        config_to_delete = await self.get_by_id(config_id)
        if not config_to_delete:
            raise DoesNotExist("配置不存在")

        config_to_delete = cast(SystemConfig, config_to_delete)
        if config_to_delete.is_readonly:
            raise ForbiddenException("只读配置项不允许删除")

        await self.dao.delete_by_id(config_id)


class UserSettingsService(BaseService):
    """用户配置服务"""

    def __init__(self):
        super().__init__(UserSettings)

    async def get_by_user_id(self, user_id: int) -> UserSettings | None:
        """通过用户ID获取配置"""
        settings_model = await self.dao.get_one(user_id=user_id)
        return cast(UserSettings, settings_model) if settings_model else None

    async def create_or_update_by_user_id(self, user_id: int, settings_in: UserSettingsUpdate) -> UserSettings:
        """通过用户ID创建或更新配置"""
        settings_data = settings_in.model_dump(exclude_unset=True)
        # obj, created
        settings_model, _ = await self.dao.update_or_create(defaults=settings_data, user_id=user_id)
        return cast(UserSettings, settings_model)


# 服务实例
role_service = RoleService()
permission_service = PermissionService()
menu_service = MenuService()
operation_log_service = OperationLogService()
login_log_service = LoginLogService()
system_config_service = SystemConfigService()
user_settings_service = UserSettingsService()
